﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using Ignitron.EEngine.Adapters.DX.System;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Buffer = SharpDX.Direct3D11.Buffer;
using Device = SharpDX.Direct3D11.Device;


namespace Ignitron.EEngine.Adapters.DX.Graphics
{
    /// <summary>
    /// Shader used for coloured output - used mainly to draw lines, points and other primitives
    /// </summary>
    internal class ColorShader: ShaderBase
    {
        /// <summary>
        /// Vertex structure 
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        internal struct Vertex
        {
            public Vector4 position;
            public Vector4 color;
        }

        #region Initialization and Shutdown

        /// <summary>
        /// Creates the structure of input elements for the shader
        /// </summary>
        protected override InputElement[] CreateInputElements()
        {
            // Now setup the layout of the data that goes into the shader.
            // This setup needs to match the VertexType structure in the Model and in the shader.
            var inputElements = new InputElement[]
				{
					new InputElement()
					{
						SemanticName = "POSITION",
						SemanticIndex = 0,
						Format = Format.R32G32B32A32_Float,
						Slot = 0,
						AlignedByteOffset = 0,
						Classification = InputClassification.PerVertexData,
						InstanceDataStepRate = 0
					},
					new InputElement()
					{
						SemanticName = "COLOR",
						SemanticIndex = 0,
						Format = Format.R32G32B32A32_Float,
						Slot = 0,
						AlignedByteOffset = ShaderUtilities.Vector4Alignment,
						Classification = InputClassification.PerVertexData,
						InstanceDataStepRate = 0
					}
				};

            return inputElements;
        }

        /// <summary>
        /// Initializes the shader
        /// </summary>
        protected override bool InitializeShader(SharpDX.Direct3D11.Device device, IntPtr windowHandler, string vsFileName, string psFileName)
        {
            try
            {
                CompileShaders(device, windowHandler, vsFileName, psFileName, "ColorVertexShader", "ColorPixelShader");

                // Setup the description of the dynamic matrix constant buffer that is in the vertex shader.
                var matrixBufferDesc = new BufferDescription()
                {
                    Usage = ResourceUsage.Dynamic,
                    SizeInBytes = Utilities.SizeOf<Matrix>(),
                    BindFlags = BindFlags.ConstantBuffer,
                    CpuAccessFlags = CpuAccessFlags.Write,
                    OptionFlags = ResourceOptionFlags.None,
                    StructureByteStride = 0
                };

                // Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
                ConstantMatrixBuffer = new Buffer(device, matrixBufferDesc);

                return true;
            }
            catch (Exception)
            {
                //MessageBox.Show("Error initializing shader. Error is " + ex.Message);
                return false;
            };
        }

        /// <summary>
        /// Relases the shader
        /// </summary>
        protected override void ShuddownShader()
        {
            base.ShuddownShader();
        }

        #endregion


        public bool Render(int vertexCount)
        {
            // Set the shader parameters that it will use for rendering.
            if (!SetShaderParameters())
                return false;

            // Now render the prepared buffers with the shader.
            RenderUnindexed(mDevice.ImmediateContext, vertexCount);

            return true;
        }

        private bool SetShaderParameters()
        {
            try
            {
                var deviceContext = mDevice.ImmediateContext;

                // copy values
                var worldMatrix = mWorldMatrix;
                var viewMatrix = mViewMatrix;
                var projectionMatrix = mProjectionMatrix;

                // Transpose the matrices to prepare them for shader.
                worldMatrix.Transpose();
                viewMatrix.Transpose();
                projectionMatrix.Transpose();

                // Lock the constant buffer so it can be written to.
                DataStream mappedResource;
                deviceContext.MapSubresource(ConstantMatrixBuffer, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out mappedResource);

                // Copy the matrices into the constant buffer.
                var matrixBuffer = new MatrixBuffer()
                {
                    world = worldMatrix,
                    view = viewMatrix,
                    projection = projectionMatrix
                };
                mappedResource.Write(matrixBuffer);

                // Unlock the constant buffer.
                deviceContext.UnmapSubresource(ConstantMatrixBuffer, 0);

                // Set the position of the constant buffer in the vertex shader.
                var bufferNumber = 0;

                // Finally set the constant buffer in the vertex shader with the updated values.
                deviceContext.VertexShader.SetConstantBuffer(bufferNumber, ConstantMatrixBuffer);

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}
